package com.uinnova.product.eam.service.dm.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.EamArtifact;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.EamArtifactVo;
import com.uinnova.product.eam.model.asset.EamCiRltDTO;
import com.uinnova.product.eam.model.bm.SavePrivateBatchCIContext;
import com.uinnova.product.eam.model.diagram.DiagramNodeJson;
import com.uinnova.product.eam.model.dm.CheckResponse;
import com.uinnova.product.eam.model.dm.DataModelAttribute;
import com.uinnova.product.eam.model.dm.DataModelDataType;
import com.uinnova.product.eam.model.dm.DataModelEntity;
import com.uinnova.product.eam.model.dm.bean.AttrParamDto;
import com.uinnova.product.eam.model.dm.bean.CoverDiagramTypeVO;
import com.uinnova.product.eam.model.dm.bean.DataModelEntityNodeVo;
import com.uinnova.product.eam.model.dm.bean.DataTypeDto;
import com.uinnova.product.eam.model.enums.ArtifactEnum;
import com.uinnova.product.eam.model.enums.ConvertERDiagram;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamArtifactSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.dm.DataModelCatalogSvc;
import com.uinnova.product.eam.service.impl.IamsCIPrivateSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltSwitchSvc;
import com.uinnova.product.eam.service.utils.DataModelDiagramUtil;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiClass;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CiGroupPage;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.diagram.ESDiagramMoveCdt;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.base.diagram.comm.model.ESDiagramLink;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uinnova.project.base.diagram.enums.DiagramCopyEnum;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.business.BindCiRltRequestDto;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.bean.permission.base.SysUser;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wcl
 */
@Slf4j
@Service
public class DataModelCatalogSvcImpl implements DataModelCatalogSvc {

    @Resource
    private IEamArtifactSvc artifactSvc;

    @Resource
    private ESDiagramApiClient diagramApiClient;

    @Resource
    private ICISwitchSvc iciSwitchSvc;

    @Resource
    private IamsCIPrivateSvc ciPrivateSvc;

    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;

    @Resource
    private IamsCIRltSwitchSvc rltSwitchSvc;

    @Resource
    private IRltClassApiSvc rltClassApiSvc;

    private static final String LOGIC = "企业级";
    private static final String SYSTEM = "系统级";

    private static final String LOGICENTITY = "LOGICENTITY";
    private static final String PHYSICALENTITY = "PHYSICALENTITY";


    @Override
    public Boolean convertDiagrams( List<ESDiagram> diagrams,Integer convertType,Integer operation) {
        //研究下枚举类的定义；
        if(BinaryUtils.isEmpty(diagrams)){
            return false;
        }
        String ownerCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //实体属性分类
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        //获取包含关系的分类id
        CcCiClassInfo includeRlt = rltClassApiSvc.getRltClassByName(SysUtil.getCurrentUserInfo().getDomainId(), Env.DM_INCLUDE);
        CcCiClassInfo derivationRlt = rltClassApiSvc.getRltClassByName(SysUtil.getCurrentUserInfo().getDomainId(), Env.DERIVATION);

        if (BinaryUtils.isEmpty(includeRlt)) {
            throw new ServerException(Env.DM_INCLUDE +"关系分类不存在，请联系管理员创建");
        }
        if (BinaryUtils.isEmpty(derivationRlt)) {
            throw new ServerException(Env.DERIVATION +"关系分类不存在，请联系管理员创建");
        }
        //校验制品中类型是否有逻辑实体关系图的制品
        EamArtifact artifact = new EamArtifact();
        artifact.setDataStatus(1);
        artifact.setReleaseState(1);
        String name = null;
        String index = null;
        String suffixName = null;
        String nextCode = null;
        String nowCode = null;
        if(ArtifactEnum.CONCEPTUAL_ENTITY.getArtifactType().intValue() == convertType){
            nowCode = Env.CONCEPTION_ENTITY.getCode();
            nextCode = Env.LOGIC_ENTITY.getCode();
            name = "逻辑实体关系图";
            suffixName = "C";
            artifact.setTypeClassification(ArtifactEnum.RELATION_ENTITY.getArtifactType());
        }else if(ArtifactEnum.RELATION_ENTITY.getArtifactType().intValue() == convertType){
            nowCode = Env.LOGIC_ENTITY.getCode();
            nextCode = Env.LOGIC_ENTITY.getCode();
            name = "系统级逻辑实体关系图";
            index = "-C";
            suffixName = "C'";
            artifact.setTypeClassification(ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType());
        }else if(ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().intValue() == convertType){
            nowCode =  Env.LOGIC_ENTITY.getCode();
            nextCode = Env.PHYSICAL_ENTITY.getCode();
            name = "物理实体关系图";
            index = "-C'";
            suffixName = "D";
            artifact.setTypeClassification(ArtifactEnum.PHYSICAL_ENTITY.getArtifactType());
        }
        List<EamArtifact> eamArtifacts = artifactSvc.queryByConditions(artifact);
        if (BinaryUtils.isEmpty(eamArtifacts)) {
            throw new ServerException("未找到"+name+"配置信息，请检查制品类型管理！");
        }
        //取制品配置的逻辑实体关系图的 第一条数据。
        EamArtifact eamArtifact = eamArtifacts.get(0);
        CcCiClassInfo nowCiClass = ciClassApiSvc.getCIClassByCodes(nowCode);
        Long nowClassId = nowCiClass.getCiClass().getId();
        //获取转化到的分类信息
        CcCiClassInfo ciClass = ciClassApiSvc.getCIClassByCodes(nextCode);
        Long classId = ciClass.getCiClass().getId();
        String className = ciClass.getCiClass().getClassName();
        //校验er图
       /* checkNode(diagrams,ownerCode,nowClassId);*/
        for (ESDiagram esDiagram : diagrams) {
            //给视图新命名，更新视图名称后缀
            ESDiagramMoveCdt copy = EamUtil.copy(esDiagram, ESDiagramMoveCdt.class);
            if (ArtifactEnum.CONCEPTUAL_ENTITY.getArtifactType().intValue() == convertType) {
                copy.setNewName(esDiagram.getName() + "-" + suffixName);
            } else {
                String diagramName = esDiagram.getName();
                //替换后缀
                assert index != null;
                if (diagramName.endsWith(index)) {
                    String subName = diagramName.substring(0, diagramName.lastIndexOf(index));
                    copy.setNewName(subName + "-" + suffixName);
                } else {
                    //兼容旧名称规则的图/修改了名称的视图；
                    copy.setNewName(diagramName + "-" + suffixName);
                }
            }
            copy.setViewType(eamArtifact.getId().toString());
            copy.setDirType(eamArtifact.getTypeClassification());
            copy.setDiagramId(esDiagram.getDEnergy());
            copy.setCopyDiagramId(ESUtil.getUUID());
            String newEnergy = diagramApiClient.copyDiagramById(copy);
            //复制一张新视图
            List<String> convertIds = esDiagram.getConvertIds();
            if(BinaryUtils.isEmpty(convertIds)){
                convertIds = new ArrayList<>();
                convertIds.add(newEnergy);
            }else{
                convertIds.add(newEnergy);
            }
            esDiagram.setConvertIds(convertIds);
            diagramApiClient.saveOrUpdateBatch(Collections.singletonList(esDiagram));
            //将新转化的视图加密id存入上级视图中；
            Long newDiagramId = diagramApiClient.queryDiagramInfoByEnergy(newEnergy);
            List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(Collections.singletonList(newDiagramId));
            //当概念图中无实体数据，无需继续处理节点信息；
            if (BinaryUtils.isEmpty(nodeList)) {
                continue;
            }
            List<String> oldEntityCiCodes = new ArrayList<>();
            //key=属性ciCode,value=实体ciCode
            Map<String, String> map = new HashMap<>(16);
            Map<String, List<String>> oldAttrCiCodes = new HashMap<>(16);
            List<Long> nodeDel = new ArrayList<>();
            List<ESDiagramNode> nodes = new ArrayList<>();
            for (ESDiagramNode node : nodeList) {
                //非实体数据（形状），会转化到下一级图中；
                if (BinaryUtils.isEmpty(node.getCiCode())) {
                    continue;
                }
                //其他架构元素：不进行复制,在新图中删掉
                if (!node.getNodeJson().contains("items")) {
                    nodeDel.add(node.getId());
                    continue;
                }
                nodes.add(node);
                DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
                oldEntityCiCodes.add(node.getCiCode());
                List<String> attrCodes = nodeJson.getItems().stream().map(DataModelEntityNodeVo::getCiCode).filter(each -> !BinaryUtils.isEmpty(each)).collect(Collectors.toList());
                oldAttrCiCodes.put(node.getCiCode(), attrCodes);
                for (String attrCode : attrCodes) {
                    map.put(attrCode, node.getCiCode());
                }
            }
            //将视图中架构元素的节点删掉
            if (!BinaryUtils.isEmpty(nodeDel)) {
                diagramApiClient.delNodeList(nodeDel, null);
            }
            //实体he属性之间的关系
            List<BindCiRltRequestDto> rltList = new ArrayList<>();
            List<ESCIInfo> oldEntityCiList = null;
            if (!BinaryUtils.isEmpty(oldEntityCiCodes)) {
                //兼容异常情况，正常不会出现重复ciCode
                List<String> entityCodes = oldEntityCiCodes.stream().distinct().collect(Collectors.toList());
                oldEntityCiList = iciSwitchSvc.getCiByCodes(entityCodes, ownerCode, LibType.PRIVATE);
            }
            //key=原ciCode ,value=ciName
            if (BinaryUtils.isEmpty(oldEntityCiList)) {
                continue;
            }
            List<ESCIInfo> paramEntityInfo = new ArrayList<>(oldEntityCiList);
            List<String> oldPrimaryKeys = oldEntityCiList.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList());
            /**
             * 用主键查数据，
             * 如果是覆盖，将主键一致的 实体或属性更新成概念实体的数据值，这种不做过滤即可；直接保存；
             * 如果是增量，将重复的数据过滤掉，将概念图中新增的实体和属性做添加；
             */
            List<ESCIInfo> entityCi = new ArrayList<>();
            //增量
            Map<String, ESCIInfo> entityCodeMap = new HashMap<>(oldEntityCiList.size());
            //实体名称
            if(!BinaryUtils.isEmpty(operation) && operation == ConvertERDiagram.INCREMENT.getI()){
                entityCi = filterEntityCi(classId, oldPrimaryKeys, paramEntityInfo,convertType,entityCodeMap);
            }
            //覆盖
            if(BinaryUtils.isEmpty(operation) ||operation == ConvertERDiagram.COVER.getI()){
                entityCi = oldEntityCiList;
            }
            //原ciCode，实体名称map
            Map<String,ESCIInfo> incrEntityMap = new HashMap<>();
            if (!BinaryUtils.isEmpty(entityCi)) {
                incrEntityMap = entityCi.stream().collect(Collectors.toMap(ESCIInfo::getCiPrimaryKey, each -> each, (k1, k2) -> k1));
            }
            Map<String, String> entityCodeNameMap = new HashMap<>(oldEntityCiList.size());
            List<ESCIInfo> entityList = new ArrayList<>();
            List<Long> classIds = new ArrayList<>();
            Map<String, ESCIInfo> oldEntityMap = new HashMap<>();
            log.info("即将保存的新实体信息-------------->{}",JSON.toJSONString(oldEntityCiList.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList())));
            for (ESCIInfo ccCiInfo : oldEntityCiList) {
                //收集上级图中的实体信息【key=实体名称，value=实体信息】
                long uuid = ESUtil.getUUID();
                Object entityName = ccCiInfo.getAttrs().get(DataModelEntity.NAME_CN);
                if(BinaryUtils.isEmpty(entityName)){
                    throw new BinaryException("视图【"+esDiagram.getName()+"】内存在必填项【实体中文名】未填，请补全后再转化");
                }
                oldEntityMap.put(entityName.toString(), ccCiInfo);
                entityCodeNameMap.put(ccCiInfo.getCiCode(), ccCiInfo.getAttrs().get(DataModelEntity.NAME_CN).toString());
               log.info("待转化的实体信息-------------->{}",JSON.toJSONString(ccCiInfo));
                String primaryKey = ccCiInfo.getCiPrimaryKey();
                if(BinaryUtils.isEmpty(incrEntityMap.values()) ||BinaryUtils.isEmpty(incrEntityMap.get(primaryKey))){
                    continue;
                }
                ESCIInfo info = incrEntityMap.get(primaryKey);
                ESCIInfo entityInto = EamUtil.copy(info, ESCIInfo.class);
                entityInto.setId(null);
                entityInto.setCiCode(String.valueOf(uuid));
                entityInto.setClassId(classId);
                if (ArtifactEnum.CONCEPTUAL_ENTITY.getArtifactType().intValue() == convertType) {
                    entityInto.getAttrs().put(DataModelEntity.ENTITY_TYPE,LOGIC);
                    //概念->逻辑:主键无Logic，需要填上
                    JSONArray array = JSON.parseArray(primaryKey);
                    array.set(0, LOGICENTITY);
                    array.add(LOGIC);
                    entityInto.setCiPrimaryKey(array.toString());
                }
                if (ArtifactEnum.RELATION_ENTITY.getArtifactType().intValue() == convertType) {
                    entityInto.getAttrs().put(DataModelEntity.ENTITY_TYPE, SYSTEM);
                    entityInto.setCiPrimaryKey(primaryKey.replace(LOGIC, SYSTEM));
                }
                if (ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().intValue() == convertType) {
                    entityInto.getAttrs().remove(DataModelEntity.ENTITY_TYPE);
                    JSONArray array = JSON.parseArray(primaryKey);
                    array.set(0, PHYSICALENTITY);
                    array.remove(SYSTEM);
                    entityInto.setCiPrimaryKey(array.toString());
                }
                entityList.add(entityInto);
                classIds.add(classId);
            }
            //保存实体信息：key是实体的ciCode
            Map<String, SavePrivateBatchCIContext> newEntityMap = new HashMap<>();
            if(!BinaryUtils.isEmpty(entityList)){
                log.info("即将保存的新实体信息-------------->{}",JSON.toJSONString(entityList.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList())));
                newEntityMap = ciPrivateSvc.saveOrUpdateBatchCI(entityList, classIds, ownerCode, ownerCode);
            }
            //新实体的中文名称，实体信息；
            Map<String, SavePrivateBatchCIContext> entityNameMap = newEntityMap.values().stream().collect(Collectors.toMap(each -> each.getAttrsStr().get(DataModelEntity.NAME_CN), each -> each, (k1, k2) -> k1));
            for (String entityName : entityNameMap.keySet()) {
                //旧实体信息
                ESCIInfo oldCiInfo = oldEntityMap.get(entityName);
                //新实体信息
                SavePrivateBatchCIContext newCiInfo = entityNameMap.get(entityName);
                ESCIInfo esciInfo = new ESCIInfo();
                esciInfo.setId(newCiInfo.getEsCi().getId());
                esciInfo.setCiCode(newCiInfo.getEsCi().getCiCode());
                entityCodeMap.put(oldCiInfo.getCiCode(),esciInfo);
                BindCiRltRequestDto rltDto = new BindCiRltRequestDto();
                rltDto.setOwnerCode(ownerCode);
                rltDto.setRltClassId(derivationRlt.getCiClass().getId());
                rltDto.setSourceCiId(oldCiInfo.getId());
                rltDto.setSourceCiCode(oldCiInfo.getCiCode());
                rltDto.setTargetCiCode(newCiInfo.getCiCode());
                rltDto.setTargetCiId(newCiInfo.getEsCi().getId());
                rltList.add(rltDto);
            }
            //保存属性信息
            List<String> attrCiCodes = oldAttrCiCodes.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
             //key=新ciCode,value= 新ci信息；
            Map<String,ESCIInfo> newAttrCiMap = new HashMap<>();
            //概念实体属性ciCode-UUID
            Map<String, String> oldAttrCodeMap = new HashMap<>();
            if (!BinaryUtils.isEmpty(attrCiCodes)) {
                //兼容异常情况，正常不会出现重复ciCode
                List<String> attrCodes = attrCiCodes.stream().distinct().collect(Collectors.toList());
                List<ESCIInfo> attrCiInfoList = iciSwitchSvc.getCiByCodes(attrCodes, ownerCode, LibType.PRIVATE);
                List<String> oldAttrPrimaryKeys = attrCiInfoList.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList());
                /**
                 * 处理属性，覆盖or增量
                 * 增量：只管新增，不光更新，也是根据主键去查，
                 * 覆盖，以概念图为准，直接覆盖数据，主键信息一样的，覆盖成概念图，新增的，正常新增
                 */
                List<ESCIInfo> attrCiList = new ArrayList<>();
                //增量
                if(!BinaryUtils.isEmpty(operation) && operation == ConvertERDiagram.INCREMENT.getI()){
                    attrCiList = filterAttrCi(attrClass.getCiClass().getId(), oldAttrPrimaryKeys, attrCiInfoList,entityCodeMap,oldAttrCodeMap,newAttrCiMap);
                }
                //覆盖
                if(BinaryUtils.isEmpty(operation) || operation == ConvertERDiagram.COVER.getI()){
                    attrCiList = attrCiInfoList;
                }
                //此时若图中的属性已被删掉
                if (!BinaryUtils.isEmpty(attrCiList)) {
                    List<ESCIInfo> attrList = new ArrayList<>();
                    log.info("待转化的属性信息-------------->{}",JSON.toJSONString(attrCiList.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList())));
                    for (ESCIInfo ccCiInfo : attrCiList) {
                        ESCIInfo newAttr = EamUtil.copy(ccCiInfo, ESCIInfo.class);
                        //旧实体的ciCOde
                        String oldEntityCode = map.get(newAttr.getCiCode());
                        //新实体的ciCOde
                        String newCiCode = entityCodeMap.get(oldEntityCode).getCiCode();
                        String uuid = String.valueOf(ESUtil.getUUID());
                        oldAttrCodeMap.put(ccCiInfo.getCiCode(), uuid);
                        newAttr.setId(null);
                        newAttr.setCiCode(uuid);
                        newAttr.setClassId(attrClass.getCiClass().getId());
                        //绑定得是实体得ciCode
                        newAttr.getAttrs().put(DataModelAttribute.ENTITY, newCiCode);
                        JSONArray array = JSON.parseArray(ccCiInfo.getCiPrimaryKey());
                        array.add(2,newCiCode);
                        newAttr.setCiPrimaryKey(array.toString());
                        attrList.add(newAttr);
                        classIds.add(attrClass.getCiClass().getId());
                    }
                    if(!BinaryUtils.isEmpty(attrList)){
                        log.info("即将保存的新属性信息-------------->{}",JSON.toJSONString(attrList.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList())));
                        Map<String, SavePrivateBatchCIContext> newAttrMap = ciPrivateSvc.saveOrUpdateBatchCI(attrList, classIds, ownerCode, ownerCode);
                        for (SavePrivateBatchCIContext value : newAttrMap.values()) {
                            newAttrCiMap.put(value.getEsCi().getCiCode(),value.getEsCi());
                        }
                    }
                }
            }
            if(!BinaryUtils.isEmpty(nodes)){
                List<ESDiagramNode> newNodeList = new ArrayList<>(nodes.size());
                for (ESDiagramNode node : nodes) {
                    if (BinaryUtils.isEmpty(node.getCiCode())) {
                        continue;
                    }
                    //新更新的实体的ciCode和id
                    ESCIInfo newEntity = entityCodeMap.get(node.getCiCode());
                    if(BinaryUtils.isEmpty(newEntity)){
                        //增量处理，概念图中新增了实体
                        continue;
                    }
                    node.setCiCode(newEntity.getCiCode());
                    if (BinaryUtils.isEmpty(node.getNodeJson())) {
                        newNodeList.add(node);
                        continue;
                    }
                    DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
                    nodeJson.setCiCode(newEntity.getCiCode());
                    String ciPrimaryKey = nodeJson.getCiPrimaryKey();
                    log.info("node节点中的主键字段值"+ciPrimaryKey);
                    if(ArtifactEnum.CONCEPTUAL_ENTITY.getArtifactType().intValue() == convertType){
                        String newPrimary = ciPrimaryKey.replace("CONCEPTIONENTITY", LOGICENTITY);
                        nodeJson.setCiPrimaryKey(newPrimary+","+LOGIC);
                    }
                    if(ArtifactEnum.RELATION_ENTITY.getArtifactType().intValue() == convertType){
                        nodeJson.setCiPrimaryKey(ciPrimaryKey.replace(LOGIC,SYSTEM));
                    }
                    if(ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().intValue() == convertType){
                        String[] split = ciPrimaryKey.split(",");
                        split[0] = PHYSICALENTITY;
                        String substring = null;
                        //删除最后一个System
                        if(split.length <= 1){
                            log.error("ciPrimaryKey数据可能有问题"+ciPrimaryKey);
                             substring = split[0];
                        }else{
                             substring = split[0]+","+split[1];
                        }
                        nodeJson.setCiPrimaryKey(substring);
                    }
                    nodeJson.setCiId(newEntity.getId());
                    nodeJson.setClassId(classId);
                    nodeJson.setClassName(className);
                    nodeJson.setClassCode(nextCode);
                    if(!BinaryUtils.isEmpty(nodeJson.getItems())){
                        List<DataModelEntityNodeVo> items = new ArrayList<>();
                        for (DataModelEntityNodeVo item : nodeJson.getItems()) {
                            if(BinaryUtils.isEmpty(item.getCiCode())){
                                continue;
                            }
                            String newCiCode = oldAttrCodeMap.get(item.getCiCode());
                            //uuid可能为null：原因是转换的图中的属性被删除了，但节点中的数据还在，故而此数据无效，将无效数据删掉；
                            if(BinaryUtils.isEmpty(newCiCode)){
                                items.add(item);
                                continue;
                            }
                            if(BinaryUtils.isEmpty(newAttrCiMap)){
                                continue;
                            }
                            ESCIInfo newAttr = newAttrCiMap.get(newCiCode);
                            //概念实体中新增了属性
                            if(BinaryUtils.isEmpty(newAttr)){
                                continue;
                            }
                            item.setCiCode(newAttr.getCiCode());
                            item.getAttrs().put(DataModelAttribute.ENTITY, newEntity.getCiCode());
                            BindCiRltRequestDto rltDto = new BindCiRltRequestDto();
                            rltDto.setOwnerCode(ownerCode);
                            rltDto.setRltClassId(includeRlt.getCiClass().getId());
                            rltDto.setSourceCiId(newEntity.getId());
                            rltDto.setSourceCiCode(newEntity.getCiCode());
                            rltDto.setTargetCiCode(newAttr.getCiCode());
                            rltDto.setTargetCiId(newAttr.getId());
                            rltList.add(rltDto);
                        }
                        //将无效数据移除；
                        nodeJson.getItems().removeAll(items);
                    }
                    node.setNodeJson(JSON.toJSONString(nodeJson));
                    newNodeList.add(node);
                }
                //批量保存node节点,保存实体信息
                diagramApiClient.saveNodeList(newNodeList);
            }
            //批量保存画布上实体和属性之前的关系
            rltSwitchSvc.bindCiRltBatch(rltList, LibType.PRIVATE);
            List<ESDiagramLink> diagramLinks = diagramApiClient.selectLinkByDiagramIds(Collections.singletonList(newDiagramId));
            replaceLinkInfo(diagramLinks, entityCodeNameMap, entityNameMap);
        }
        return true;
    }

    @Override
    public Integer convert(Long dirId, String diagramId, Integer convertType, Integer operation) {
        List<ESDiagram> oriDiagramList = this.filterDiagramByType(dirId, diagramId, convertType);
        if(BinaryUtils.isEmpty(oriDiagramList)){
            return 0;
        }
        SysUser user = SysUtil.getCurrentUserInfo();
        String ownerCode = user.getLoginCode();
        //实体属性分类
        List<String> classCodes = Lists.newArrayList(Env.ATTRIBUTES.getCode(), Env.CONCEPTION_ENTITY.getCode(), Env.LOGIC_ENTITY.getCode(), Env.PHYSICAL_ENTITY.getCode());
        List<CcCiClassInfo> classList = ciClassApiSvc.getByClassCodes(classCodes, user.getDomainId());
        Map<String, CcCiClass> classMap = classList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassCode, e -> e, (k1, k2) -> k2));
        //获取包含关系的分类id
        CcCiClassInfo includeRlt = rltClassApiSvc.getRltClassByName(user.getDomainId(), Env.DM_INCLUDE);
        CcCiClassInfo deriveRlt = rltClassApiSvc.getRltClassByName(user.getDomainId(), Env.DERIVATION);

        if (BinaryUtils.isEmpty(includeRlt)) {
            throw new ServerException(Env.DM_INCLUDE +"关系分类不存在，请联系管理员创建");
        }
        if (BinaryUtils.isEmpty(deriveRlt)) {
            throw new ServerException(Env.DERIVATION +"关系分类不存在，请联系管理员创建");
        }
        CoverDiagramTypeVO coverVo = new CoverDiagramTypeVO(convertType);
        coverVo.setIncludeRlt(includeRlt.getCiClass());
        coverVo.setDeriveRlt(deriveRlt.getCiClass());
        //校验制品中类型是否有逻辑实体关系图的制品
        List<EamArtifactVo> coverArtifactList = artifactSvc.queryByType(Collections.singletonList(coverVo.getCoverType()));
        if (CollectionUtils.isEmpty(coverArtifactList)) {
            throw new ServerException("未找到转换的制品,请检查制品类型管理!");
        }
        coverVo.setClassMap(classMap);
        coverVo.setArtifact(coverArtifactList.get(0));
        coverVo.setOperation(operation);
        coverVo.setOwnerCode(ownerCode);
        try{
            coverDiagram(oriDiagramList, coverVo);
        }catch (Exception e){
            log.error("ER图转换失败:{}", e.getMessage());
            return -2;
        }
        return -1;
    }

    private void coverDiagram(List<ESDiagram> oriDiagramList, CoverDiagramTypeVO coverVo){
        if(CollectionUtils.isEmpty(oriDiagramList)){
            return;
        }
        Map<String, String> copyDiagramMap = this.copyDiagramBatch(oriDiagramList, coverVo);

        List<ESDiagram> copyDiagramList = diagramApiClient.selectByIds(copyDiagramMap.values(), null, Collections.singletonList(0));
        List<Long> diagramIds = copyDiagramList.stream().map(ESDiagram::getId).collect(Collectors.toList());
        List<ESDiagramNode> diagramNodes = diagramApiClient.selectNodeByDiagramIds(diagramIds);
        List<ESDiagramLink> diagramLinks = diagramApiClient.selectLinkByDiagramIds(diagramIds);
        Set<String> nodeCiCodes = DataModelDiagramUtil.getNodeCiCode(diagramNodes);
        Set<String> linkCiCodes = DataModelDiagramUtil.getLinkRltCode(diagramLinks);
        if (CollectionUtils.isEmpty(nodeCiCodes)) {
            return;
        }
        List<ESCIInfo> oriCiList = iciSwitchSvc.getCiByCodes(new ArrayList<>(nodeCiCodes), coverVo.getOwnerCode(), LibType.PRIVATE);
        CcCiClass entityClass = coverVo.getClassMap().get(coverVo.getClassCode());
        CcCiClass attrClass = coverVo.getClassMap().get(Env.ATTRIBUTES.getCode());
        //1.先取出所有实体
        Map<String, ESCIInfo> entityMap = new HashMap<>();
        Map<String, List<ESCIInfo>> entityAttrMap = new HashMap<>();
        for (ESCIInfo each : oriCiList) {
            //非实体对象
            if(each.getClassId().equals(entityClass.getId())){
                entityMap.put(each.getCiCode(), each);
            }else if(each.getClassId().equals(attrClass.getId())){
                String entityCode = each.getAttrs().getOrDefault(DataModelAttribute.ENTITY, "").toString();
                entityAttrMap.computeIfAbsent(entityCode, key -> new ArrayList<>()).add(each);
            }
        }
        if(CollectionUtils.isEmpty(entityMap)){
            return;
        }
        //原实体code->新实体code
        Map<String, String> copyEntityMap = this.copyEntity(entityMap, coverVo);
        //原实体属性code->新实体属性code
        Map<String, String> copyAttrMap = this.copyAttribute(entityAttrMap, copyEntityMap, coverVo);
        Set<String> ciCodes = new HashSet<>();
        ciCodes.addAll(copyEntityMap.values());
        ciCodes.addAll(copyAttrMap.values());
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(new ArrayList<>(ciCodes), coverVo.getOwnerCode(), LibType.PRIVATE);
        ciList.addAll(oriCiList);
        Map<String, ESCIInfo> ciMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, e -> e, (k1, k2) -> k2));
        //更新视图节点信息
        updateNodeInfo(diagramNodes, copyEntityMap, copyAttrMap, ciMap, entityClass);
        //绑定三种关系，实体及实体属性，实体与实体之间，衍生
        List<BindCiRltRequestDto> entityRltList = bindLinkRlt(diagramLinks, copyEntityMap, ciMap, coverVo.getOwnerCode());
        //绑定实体及实体属性关系
        List<BindCiRltRequestDto> attrRltList = bindEntityAttrRlt(copyAttrMap.values(), ciMap, coverVo);
        entityRltList.addAll(attrRltList);
        List<BindCiRltRequestDto> deriveRltList = bindEntityDeriveRlt(copyEntityMap, ciMap, coverVo);
        entityRltList.addAll(deriveRltList);
        rltSwitchSvc.bindCiRltBatch(entityRltList, LibType.PRIVATE);
    }

    /**
     * 绑定实体间衍生关系
     * @param copyEntityMap 原ciCode->新ciCode 映射
     * @param ciMap ci对象集合
     * @param coverVo 参数
     * @return 绑定关系
     */
    private List<BindCiRltRequestDto> bindEntityDeriveRlt(Map<String, String> copyEntityMap, Map<String, ESCIInfo> ciMap, CoverDiagramTypeVO coverVo) {
        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        Long rltId = coverVo.getDeriveRlt().getId();
        for (Map.Entry<String, String> entry : copyEntityMap.entrySet()) {
            ESCIInfo source = ciMap.get(entry.getKey());
            ESCIInfo target = ciMap.get(entry.getValue());
            if(source == null || target == null){
                continue;
            }
            BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(coverVo.getOwnerCode()).repetitionError(false)
                    .rltClassId(rltId).sourceCiId(source.getId()).targetCiId(target.getId()).build();
            bindList.add(bindRlt);
        }
        return bindList;
    }

    /**
     * 绑定实体及实体属性关系
     * @param attrList 新实体属性code
     * @param ciMap ci集合
     * @param coverVo 参数
     * @return 关系
     */
    private List<BindCiRltRequestDto> bindEntityAttrRlt(Collection<String> attrList, Map<String, ESCIInfo> ciMap, CoverDiagramTypeVO coverVo) {
        Long includeRltId = coverVo.getIncludeRlt().getId();
        Map<String, List<ESCIInfo>> entityAttrMap = new HashMap<>();
        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        for (String ciCode : attrList) {
            ESCIInfo attribute = ciMap.get(ciCode);
            if(attribute == null){
                log.error("未获取到新实体属性:{}", ciCode);
                continue;
            }
            Object entityCodeObj = attribute.getAttrs().get(DataModelAttribute.ENTITY);
            if(entityCodeObj == null || ciMap.get(entityCodeObj.toString()) == null){
                log.error("未获取到实体属性的关联实体:{}", entityCodeObj);
                continue;
            }
            entityAttrMap.computeIfAbsent(entityCodeObj.toString(), key -> new ArrayList<>()).add(attribute);
        }
        if(entityAttrMap.isEmpty()){
            return bindList;
        }
        for (Map.Entry<String, List<ESCIInfo>> entry : entityAttrMap.entrySet()) {
            for (ESCIInfo attribute : entry.getValue()) {
                ESCIInfo entity = ciMap.get(entry.getKey());
                BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(coverVo.getOwnerCode()).repetitionError(false)
                        .rltClassId(includeRltId).custom1("3").sourceCiId(entity.getId()).targetCiId(attribute.getId()).build();
                bindList.add(bindRlt);
            }
        }
        return bindList;
    }

    /**
     * 获取实体间关系信息并更新link
     * @param diagramLinks 视图关系线
     * @param copyEntityMap 原ciCode->新ciCode 映射
     * @param ciMap ciCode->ciInfo
     * @param ownerCode 用户
     * @return 实体关系集合
     */
    private List<BindCiRltRequestDto> bindLinkRlt(List<ESDiagramLink> diagramLinks, Map<String, String> copyEntityMap, Map<String, ESCIInfo> ciMap, String ownerCode) {
        //绑定实体间关系并更新link信息
        List<ESDiagramLink> saveLinkList = new ArrayList<>();
        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        for (ESDiagramLink link : diagramLinks) {
            if(BinaryUtils.isEmpty(link.getUniqueCode())){
                continue;
            }
            List<String> codes = Arrays.asList(link.getUniqueCode().split("_"));
            String sourceCode = copyEntityMap.get(codes.get(1));
            String targetCode = copyEntityMap.get(codes.get(3));
            if(sourceCode == null || targetCode == null){
                log.error("关系更新错误,新ciCode未找到:{},{}", codes.get(1), codes.get(3));
                continue;
            }
            ESCIInfo source = ciMap.get(sourceCode);
            ESCIInfo target = ciMap.get(targetCode);
            if(source == null || target == null){
                log.error("关系更新错误,新ci未找到:{},{}", codes.get(1), codes.get(3));
                continue;
            }
            JSONObject linkJson = JSON.parseObject(link.getLinkJson());
            Long classId = linkJson.getLong("classId");
            BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(ownerCode).repetitionError(false)
                    .rltClassId(classId).sourceCiId(source.getId()).targetCiId(target.getId()).build();
            bindList.add(bindRlt);
            link.setUniqueCode("UK_" + sourceCode + "_" + classId + "_" + targetCode);
            linkJson.put("rltCode", link.getUniqueCode());
            link.setLinkJson(linkJson.toJSONString());
            saveLinkList.add(link);
        }
        if(!CollectionUtils.isEmpty(saveLinkList)){
            diagramApiClient.saveLinkList(saveLinkList);
        }
        return bindList;
    }

    /**
     * 视图转换->视图复制
     * @param diagramList 原视图集合
     * @param coverVo 转换参数
     * @return 原视图id->新视图id 映射
     */
    private Map<String, String> copyDiagramBatch(List<ESDiagram> diagramList, CoverDiagramTypeVO coverVo){
        Map<String, Long> diagramDirMap = new HashMap<>();
        List<ESDiagram> copyList = new ArrayList<>();
        for (ESDiagram each : diagramList) {
            diagramDirMap.put(each.getDEnergy(), each.getDirId());
            ESDiagram copy = EamUtil.copy(each, ESDiagram.class);
            copy.setName(each.getName().replace(coverVo.getSuffix(), "") + coverVo.getCoverSuffix());
            copy.setViewType(coverVo.getArtifact().getId().toString());
            copy.setDirType(coverVo.getArtifact().getTypeClassification());
            copyList.add(copy);
        }
        Map<String, String> copyDiagramMap = diagramApiClient.copyDiagramBatch(diagramDirMap, copyList, DiagramCopyEnum.COMMON);
        for (ESDiagram each : diagramList) {
            List<String> convertIds = each.getConvertIds();
            if(BinaryUtils.isEmpty(convertIds)){
                convertIds = new ArrayList<>();
            }
            convertIds.add(copyDiagramMap.get(each.getDEnergy()));
            each.setConvertIds(convertIds);
        }
        diagramApiClient.saveOrUpdateBatch(diagramList);
        return copyDiagramMap;
    }

    /**
     * 复制实体
     * @param entityMap 原实体ciCode->ci 映射
     * @param coverVo 参数
     * @return 原ciCode->新ciCode 映射
     */
    private Map<String, String> copyEntity(Map<String, ESCIInfo> entityMap, CoverDiagramTypeVO coverVo) {
        CcCiClass coverClass = coverVo.getClassMap().get(coverVo.getCoverCode());
        Set<String> primaryKeys = new HashSet<>();
        List<ESCIInfo> copyList = new ArrayList<>();
        for (Map.Entry<String, ESCIInfo> each : entityMap.entrySet()) {
            ESCIInfo entity = each.getValue();
            ESCIInfo copy = EamUtil.copy(entity, ESCIInfo.class);
            //主键、label
            copy.setId(null);
            copy.setClassId(coverClass.getId());
            JSONArray primaryKey = JSON.parseArray(entity.getCiPrimaryKey());
            primaryKey.set(0, coverClass.getClassStdCode());
            if(ArtifactEnum.RELATION_ENTITY.getArtifactType().equals(coverVo.getCoverType())){
                primaryKey.add(LOGIC);
                copy.getAttrs().put(DataModelEntity.ENTITY_TYPE, LOGIC);
            }else if(ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().equals(coverVo.getCoverType())){
                primaryKey.set(primaryKey.indexOf(LOGIC), SYSTEM);
                copy.getAttrs().put(DataModelEntity.ENTITY_TYPE, SYSTEM);
            }else{
                primaryKey.remove(SYSTEM);
                copy.getAttrs().remove(DataModelEntity.ENTITY_TYPE);
            }
            copy.setCiPrimaryKey(primaryKey.toJSONString());
            if(primaryKeys.contains(copy.getCiPrimaryKey())){
                continue;
            }
            copy.setLocalVersion(1);
            copy.setPublicVersion(0);
            primaryKeys.add(copy.getCiPrimaryKey());
            copyList.add(copy);
        }
        return saveCopyCiList(copyList, primaryKeys, coverClass.getId(), coverVo);
    }

    /**
     * 复制实体属性
     * @param entityAttrMap 原实体ciCode->实体属性集合 映射
     * @param copyEntityMap 原实体ciCode->新实体ciCode 映射
     * @param coverVo 参数
     * @return 原ciCode->新ciCode 映射
     */
    private Map<String, String> copyAttribute(Map<String, List<ESCIInfo>> entityAttrMap, Map<String, String> copyEntityMap, CoverDiagramTypeVO coverVo) {
        List<ESCIInfo> copyList = new ArrayList<>();
        Set<String> primaryKeys = new HashSet<>();
        for (Map.Entry<String, List<ESCIInfo>> each : entityAttrMap.entrySet()) {
            List<ESCIInfo> attrs = each.getValue();
            //转换后的实体ciCode
            String coverEntityCode = copyEntityMap.get(each.getKey());
            if(CollectionUtils.isEmpty(attrs) || BinaryUtils.isEmpty(coverEntityCode)){
                log.error("实体属性未关联到转换后的实体code:{}", each.getKey());
                continue;
            }
            for (ESCIInfo attr : attrs) {
                ESCIInfo copy = EamUtil.copy(attr, ESCIInfo.class);
                copy.setId(null);
                copy.setLocalVersion(1);
                copy.setPublicVersion(0);
                copy.setCiPrimaryKey(copy.getCiPrimaryKey().replaceAll(each.getKey(), coverEntityCode));
                copy.getAttrs().put(DataModelAttribute.ENTITY, coverEntityCode);
                if(primaryKeys.contains(copy.getCiPrimaryKey())){
                    continue;
                }
                primaryKeys.add(copy.getCiPrimaryKey());
                copyList.add(copy);
            }
        }
        CcCiClass attrClass = coverVo.getClassMap().get(Env.ATTRIBUTES.getCode());
        return saveCopyCiList(copyList, primaryKeys, attrClass.getId(), coverVo);
    }

    /**
     * 保存复制的ci信息
     * @param copyList 复制的ci集合
     * @param primaryKeys 业务主键集合
     * @param classId 分类id
     * @param coverVo 参数
     * @return 原ciCode->新ciCode 映射
     */
    private Map<String, String> saveCopyCiList(List<ESCIInfo> copyList, Set<String> primaryKeys, Long classId, CoverDiagramTypeVO coverVo){
        List<ESCIInfo> ciByPrimaryKeys = iciSwitchSvc.getCiByPrimaryKeys(primaryKeys, coverVo.getOwnerCode(), LibType.PRIVATE);
        Map<String, ESCIInfo> ciPrimaryKeyMap = ciByPrimaryKeys.stream().collect(Collectors.toMap(CcCi::getCiPrimaryKey, e -> e, (k1, k2) -> k2));
        Map<String, String> result = new HashMap<>();
        List<ESCIInfo> saveList = new ArrayList<>();
        for (ESCIInfo each : copyList) {
            ESCIInfo existEntity = ciPrimaryKeyMap.get(each.getCiPrimaryKey());
            String ciCode = each.getCiCode();
            //做增量且已存在
            if(!coverVo.getOperation() && !BinaryUtils.isEmpty(existEntity)){
                result.put(ciCode, existEntity.getCiCode());
                continue;
            }else if(!BinaryUtils.isEmpty(existEntity)){
                each.setId(existEntity.getId());
                each.setCiCode(existEntity.getCiCode());
                each.setLocalVersion(existEntity.getLocalVersion());
                each.setPublicVersion(existEntity.getPublicVersion());
            }else{
                each.setCiCode(String.valueOf(ESUtil.getUUID()));
            }
            result.put(ciCode, each.getCiCode());
            saveList.add(each);
        }
        ciPrivateSvc.saveOrUpdateBatchCI(saveList, Collections.singletonList(classId), coverVo.getOwnerCode(), coverVo.getOwnerCode());
        return result;
    }

    /**
     * 更新node信息
     * @param nodes 视图节点
     * @param entityCodeMap 实体原ciCode->新ciCode 映射
     * @param attrCodeMap 实体属性原ciCode->新ciCode 映射
     * @param ciMap ci集合
     * @param coverClass 转换的实体分类
     */
    private void updateNodeInfo(List<ESDiagramNode> nodes, Map<String, String> entityCodeMap, Map<String, String> attrCodeMap, Map<String, ESCIInfo> ciMap, CcCiClass coverClass) {
        if(CollectionUtils.isEmpty(nodes)){
            return;
        }
        List<ESDiagramNode> saveNodeList = new ArrayList<>();
        List<Long> deleteList = new ArrayList<>();
        for (ESDiagramNode node : nodes) {
            String entityCode = node.getCiCode();
            if(BinaryUtils.isEmpty(entityCode) || !ciMap.containsKey(entityCode)){
                continue;
            }
            String coverEntityCode = entityCodeMap.get(entityCode);
            if(BinaryUtils.isEmpty(coverEntityCode)){
                log.error("未获取到copy的实体code,数据错误:{}", entityCode);
                deleteList.add(node.getId());
                continue;
            }
            node.setCiCode(coverEntityCode);
            ESCIInfo coverEntity = ciMap.get(coverEntityCode);
            if(coverEntity == null){
                log.error("未获取到copy的实体,数据错误:{}", entityCode);
                deleteList.add(node.getId());
                continue;
            }
            DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
            nodeJson.setClassCode(coverClass.getClassCode());
            nodeJson.setClassId(coverClass.getId());
            nodeJson.setClassName(coverClass.getClassName());
            nodeJson.setCiId(coverEntity.getId());
            nodeJson.setCiCode(coverEntity.getCiCode());
            nodeJson.setCiPrimaryKey(coverEntity.getCiPrimaryKey());
            if(!CollectionUtils.isEmpty(nodeJson.getItems())){
                for (DataModelEntityNodeVo item : nodeJson.getItems()) {
                    String coverAttrCode = attrCodeMap.get(item.getCiCode());
                    if(BinaryUtils.isEmpty(coverAttrCode)){
                        log.error("未获取到copy的实体属性code,数据错误:{}", item.getCiCode());
                        continue;
                    }
                    ESCIInfo coverAttr = ciMap.get(coverAttrCode);
                    if(BinaryUtils.isEmpty(coverAttr)){
                        log.error("未获取到copy的实体code,数据错误:{}", item.getCiCode());
                        continue;
                    }
                    item.setCiCode(coverAttr.getCiCode());
                    item.getAttrs().put(DataModelAttribute.ENTITY, coverEntityCode);
                }
            }
            node.setNodeJson(JSON.toJSONString(nodeJson));
            saveNodeList.add(node);
        }
        diagramApiClient.delNodeList(deleteList, null);
        diagramApiClient.saveNodeList(saveNodeList);
    }

    private List<ESCIInfo> filterAttrCi(Long attrClassId, List<String> oldAttrPrimaryKeys, List<ESCIInfo> attrList,
                                        Map<String, ESCIInfo> entityCodeMap,Map<String, String> oldAttrCodeMap,Map<String,ESCIInfo> newAttrCiMap) {
        Map<String,String> primaryKeyMap = new HashMap<>(oldAttrPrimaryKeys.size());
        List<String> primaryKeyList = new ArrayList<>();
        for (String ciPrimaryKey : oldAttrPrimaryKeys) {
            JSONArray array = JSON.parseArray(ciPrimaryKey);
            //取第三位所属实体的ciCode  ciPrimaryKey
            //"[\"ATTRIBUTES\",\"用户姓名\",\"1526329451907429\"]"
            String entityCode = array.get(2).toString();
            //根据实体code查找对应新的ciCode
            String newEntityCiCode = entityCodeMap.get(entityCode).getCiCode();
            array.remove(2);
            array.add(newEntityCiCode);
            primaryKeyMap.put(ciPrimaryKey,array.toString());
            primaryKeyList.add(array.toString());
        }
        //查询属性信息是否存在；
        SysUser userInfo = SysUtil.getCurrentUserInfo();
        ESCISearchBean bean = new ESCISearchBean();
        bean.setClassIds(Collections.singletonList(attrClassId));
        bean.setCiPrimaryKeys(primaryKeyList);
        bean.setOwnerCode(userInfo.getLoginCode());
        bean.setLibType(LibType.PRIVATE);
        bean.setPageNum(1);
        bean.setPageSize(100000);
        //查出来的是 已存在得实体数据
        CiGroupPage ciGroupPage = ciPrivateSvc.queryPageBySearchBean(bean, false);
        List<CcCiInfo> data = ciGroupPage.getData();
        log.info("已存在在即将转化得视图中得实体信息"+data.toString());
        if(!BinaryUtils.isEmpty(data)){
            List<String> repeatPrimaryList = data.stream().map(each -> each.getCi().getCiPrimaryKey()).collect(Collectors.toList());
            Map<String, CcCiInfo> newAttrMap = data.stream().collect(Collectors.toMap(each -> each.getCi().getCiCode(), each -> each, (k1, k2) -> k1));
            //key=主键；value=属性的ciCode
            Map<String, String> newCodeMap = data.stream().collect(Collectors.toMap(each -> each.getCi().getCiPrimaryKey(), each -> each.getCi().getCiCode()));
            //移除得实体信息； 这步的处理相当于增量，如果逻辑实体中有实体信息，不进行处理，没有的继续做添加
            Iterator<ESCIInfo> iterator = attrList.iterator();
            while(iterator.hasNext()){
                ESCIInfo next = iterator.next();
                String ciPrimaryKey = next.getCiPrimaryKey();
                String newPrimaryKey = primaryKeyMap.get(ciPrimaryKey);
                //key= 概念图中的ciCode,value=逻辑图中的ciCode
                if(repeatPrimaryList.contains(newPrimaryKey)){
                    String newCiCode = newCodeMap.get(newPrimaryKey);
                    ESCIInfo esInfo = new ESCIInfo();
                    esInfo.setCiCode(newCiCode);
                    esInfo.setId(newAttrMap.get(newCiCode).getCi().getId());
                    oldAttrCodeMap.put(next.getCiCode(),newCiCode);
                    newAttrCiMap.put(newCiCode,esInfo);
                    iterator.remove();
                }
            }
        }
        return attrList;
    }


    public List<ESCIInfo> filterEntityCi(Long classId,List<String> ciPrimaryKeys,List<ESCIInfo> entityList,Integer convertType, Map<String, ESCIInfo> entityCodeMap){
        Map<String,String> primaryKeyMap = new HashMap<>();
        List<String> primaryKeyList = new ArrayList<>();
        for (String ciPrimaryKey : ciPrimaryKeys) {
            if (ArtifactEnum.CONCEPTUAL_ENTITY.getArtifactType().intValue() == convertType) {
                //概念->逻辑:主键无Logic，需要填上
                JSONArray array = JSON.parseArray(ciPrimaryKey);
                array.set(0, LOGICENTITY);
                array.add(LOGIC);
                primaryKeyList.add(array.toString());
                primaryKeyMap.put(ciPrimaryKey,array.toString());
            }
            if (ArtifactEnum.RELATION_ENTITY.getArtifactType().intValue() == convertType) {
                //逻辑转系统：Logic, System
                String newPrimaryKey = ciPrimaryKey.replace(LOGIC, SYSTEM);
                primaryKeyMap.put(ciPrimaryKey,newPrimaryKey);
                primaryKeyList.add(newPrimaryKey);
            }
            if (ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().intValue() == convertType) {
                //系统转物理:将System去掉
                JSONArray array = JSON.parseArray(ciPrimaryKey);
                array.set(0, PHYSICALENTITY);
                array.remove(SYSTEM);
                primaryKeyList.add(array.toString());
                primaryKeyMap.put(ciPrimaryKey,array.toString());
            }
        }
        //拿旧实体信息去转化得位置看下，ci是否存在，存在得 就i过滤掉不进行添加了；
        SysUser userInfo = SysUtil.getCurrentUserInfo();
        ESCISearchBean bean = new ESCISearchBean();
        bean.setClassIds(Collections.singletonList(classId));
        bean.setCiPrimaryKeys(primaryKeyList);
        bean.setOwnerCode(userInfo.getLoginCode());
        bean.setLibType(LibType.PRIVATE);
        bean.setPageNum(1);
        bean.setPageSize(100000);
        //查出来的是 已存在得实体数据
        CiGroupPage ciGroupPage = ciPrivateSvc.queryPageBySearchBean(bean, false);
        List<CcCiInfo> data = ciGroupPage.getData();
        log.info("已存在在即将转化得视图中得实体信息"+data.toString());
        if(!BinaryUtils.isEmpty(data)){
            //key= 逻辑实体主键，value=逻辑实体ciCode
            Map<String, CcCiInfo> map = data.stream().collect(Collectors.toMap(each -> each.getCi().getCiPrimaryKey(), each->each));
            List<String> repeatPrimaryList = data.stream().map(each -> each.getCi().getCiPrimaryKey()).collect(Collectors.toList());
            //移除得实体信息； 这步的处理相当于增量，如果逻辑实体中有实体信息，不进行处理，没有的继续做添加
            Iterator<ESCIInfo> iterator = entityList.iterator();
            while(iterator.hasNext()){
                ESCIInfo next = iterator.next();
                String ciPrimaryKey = next.getCiPrimaryKey();
                String newPrimaryKey = primaryKeyMap.get(ciPrimaryKey);
                if(repeatPrimaryList.contains(newPrimaryKey)){
                    //收集已存在得实体的ciCode
                    CcCiInfo ciInfo = map.get(newPrimaryKey);
                    ESCIInfo esciInfo = new ESCIInfo();
                    esciInfo.setId(ciInfo.getCi().getId());
                    esciInfo.setCiCode(ciInfo.getCi().getCiCode());
                    entityCodeMap.put(next.getCiCode(),esciInfo);
                    iterator.remove();
                }
            }
        }
        return entityList;
    }
    private void replaceLinkInfo(List<ESDiagramLink> links, Map<String, String> entityCodeNameMap, Map<String, SavePrivateBatchCIContext> entityNameMap){
        //link节点数据记录了实体和实体之间的关系
        if (BinaryUtils.isEmpty(links)) {
            //如果实体与实体之间没有关系线数据，就不需要继续处理
            return;
        }
        List<ESDiagramLink> linkList = new ArrayList<>();
        for (ESDiagramLink esDiagramLink : links) {
            //源端ciCode，关系classId,目标端ciCOde
            String uniqueCode = esDiagramLink.getUniqueCode();
            if (BinaryUtils.isEmpty(uniqueCode)) {
                continue;
            }
            String[] s = uniqueCode.split("_");
            String ciName = entityCodeNameMap.get(s[1]);
            SavePrivateBatchCIContext ciContext = entityNameMap.get(ciName);
            if(BinaryUtils.isEmpty(ciContext)){
                continue;
            }
            String sourceCiCode = ciContext.getEsCi().getCiCode();
            Long sourceCiId = entityNameMap.get(ciName).getEsCi().getId();
            String targetName = entityCodeNameMap.get(s[3]);
            SavePrivateBatchCIContext targetContext = entityNameMap.get(targetName);
            if(BinaryUtils.isEmpty(targetContext)){
                continue;
            }
            String targetCiCode = targetContext.getEsCi().getCiCode();
            Long targetId = entityNameMap.get(targetName).getEsCi().getId();
            String newUniqueCode = s[0] + "_" + sourceCiCode + "_" + s[2] + "_" + targetCiCode;
            esDiagramLink.setUniqueCode(newUniqueCode);
            ESRltSearchBean cltBean = new ESRltSearchBean();
            cltBean.setPageNum(1);
            cltBean.setPageSize(1000);
            cltBean.setSourceCiCodes(Collections.singleton(s[1]));
            cltBean.setTargetCiCodes(Collections.singleton(s[3]));
            List<EamCiRltDTO> eamCiRltDTOS = rltSwitchSvc.searchRltByRltCodes(cltBean, LibType.PRIVATE);
            if(BinaryUtils.isEmpty(eamCiRltDTOS)){
                continue;
            }
            EamCiRltDTO oldRltInfo = rltSwitchSvc.searchRltByRltCodes(cltBean, LibType.PRIVATE).get(0);
            //设置实体与实体之间的关系参数
            BindCiRltRequestDto reqBean = new BindCiRltRequestDto();
            reqBean.setOwnerCode(SysUtil.getCurrentUserInfo().getLoginCode());
            reqBean.setRltClassId(Long.valueOf(s[2]));
            reqBean.setAttrs(oldRltInfo.getAttrs());
            reqBean.setSourceCiId(sourceCiId);
            reqBean.setSourceCiCode(sourceCiCode);
            reqBean.setTargetCiId(targetId);
            reqBean.setTargetCiCode(targetCiCode);
            rltSwitchSvc.bindCiRlt(reqBean, LibType.PRIVATE);
            ESRltSearchBean bean = new ESRltSearchBean();
            bean.setPageNum(1);
            bean.setPageSize(1000);
            bean.setTargetCiCodes(Collections.singleton(targetCiCode));
            bean.setSourceCiCodes(Collections.singleton(sourceCiCode));
            CcCiRltInfo ccCiRltInfo = rltSwitchSvc.searchRltByBean(bean, LibType.PRIVATE).getData().get(0);
            JSONObject link = JSON.parseObject(esDiagramLink.getLinkJson(), JSONObject.class);
            link.put("rltCode", newUniqueCode);
            link.put("rltId", ccCiRltInfo.getCiRlt().getId());
            esDiagramLink.setLinkJson(link.toString());
            linkList.add(esDiagramLink);
        }
        //批量保存link节点数据
        diagramApiClient.saveLinkList(linkList);
    }

    @Override
    public List<ESDiagram> filterDiagramByType(Long dirId,String dEnergy,Integer type){
        List<ESDiagram> result = new ArrayList<>();
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //根据目录id查询该层级目录的视图
        List<ESDiagram> diagramList;
        if(!BinaryUtils.isEmpty(dirId)){
            diagramList = diagramApiClient.selectByDirIds(Collections.singleton(dirId), null, loginCode, Collections.singletonList(0));
        }else{
            diagramList = diagramApiClient.selectByIds(Collections.singletonList(dEnergy), null, Collections.singletonList(0));
        }
        List<EamArtifactVo> artifactList = artifactSvc.queryByType(Collections.singletonList(type));
        if(BinaryUtils.isEmpty(diagramList) || BinaryUtils.isEmpty(artifactList)){
            return result;
        }
        Set<String> artifactIds = artifactList.stream().map(e -> e.getId().toString()).collect(Collectors.toSet());
        for (ESDiagram diagram : diagramList) {
            String viewType = diagram.getViewType();
            if(!BinaryUtils.isEmpty(viewType) && artifactIds.contains(viewType)){
                result.add(diagram);
            }
        }
        return result;
    }

    @Override
    public CheckResponse checkVariation(Long dirId, String dEnergy, Integer convertType) {
        CheckResponse response = new CheckResponse();
        List<ESDiagram> diagrams = filterDiagramByType(dirId, dEnergy, convertType);
        /***
         * 拿到每张视图中的节点数据，将实体和属性分离开，判断要转换的私有库是否存在，如果存在 就返回即可；
         * 只判断图中的实体 属性，【形状，其他架构元素以及关系数据是否有更新--不管】
         * 原转er图的接口，对其他架构元素 不予处理，转换的时候，不会带上；
         */
        //批量转er图时，有的图时第一次转化，有的图不是第一次转化，这种怎么去处理  -----> 目前默认只要有不是第一次转化得图，都按非第一次转化处理；
        List<String> convertIds = diagrams.stream().filter(each -> !BinaryUtils.isEmpty(each.getConvertIds())).map(ESDiagram::getConvertIds).flatMap(Collection::stream).collect(Collectors.toList());
        if(BinaryUtils.isEmpty(convertIds)){
            response.setFirst(true);
            return response;
        }
        //拿到所有的视图加密id
        List<Long> diagramIds = diagrams.stream().map(ESDiagram::getId).collect(Collectors.toList());
        //根据视图id拿到所有的node节点
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(diagramIds);
        if(BinaryUtils.isEmpty(nodeList)){
            return response;
        }
        //从node中获取到实体ciCode
        List<String> entityCiCode = new ArrayList<>();
        //获取到所有的实体中的属性信息：key=实体ciCode,value=属性ciCodes
        Map<String, List<String>> attrCiCodeMap = new HashMap<>(16);
        //旧实体ciCode，新实体ciCode
        Map<String, String> oldAndNewEntityMap = new HashMap<>();
        for (ESDiagramNode node : nodeList) {
            String ciCode = node.getCiCode();
            if (BinaryUtils.isEmpty(ciCode)) {
                //形状不管
                continue;
            }
            //收集节点中实体的ciCode
            entityCiCode.add(ciCode);
            String nodeString = node.getNodeJson();
            if (!nodeString.contains("items")) {
                //其他架构元素也不去校验更新
                continue;
            }
            DiagramNodeJson nodeJson = JSON.parseObject(nodeString, DiagramNodeJson.class);
            List<DataModelEntityNodeVo> items = nodeJson.getItems();
            if (BinaryUtils.isEmpty(items)) {
                continue;
            }
            List<String> attrCodes = items.stream().map(DataModelEntityNodeVo::getCiCode).collect(Collectors.toList());
            //收集每个实体中属性的ciCode
            attrCiCodeMap.put(ciCode, attrCodes);
        }
        // 以上已经获取到 图中所有实体 和属性的ciCode（map）
        //需要将实体和属性的信息查出来，拿到主键，和全量属性信息组成一个map
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //实体信息
        List<ESCIInfo> entityInfo = iciSwitchSvc.getCiByCodes(entityCiCode, loginCode, LibType.PRIVATE);
        /**
         * 看转化类型，拿刚收集的数据，去查对应实体和属性数据，数量和全字段属性值是
         * 先比较数量，再比较主键，主键一致，再比较全字段，
         */
        //校验实体：key = 新主键 value = 旧主键
        Map<String, ESCIInfo> entityMap = new HashMap<>();
        for (ESCIInfo info : entityInfo) {
            String ciPrimaryKey = info.getCiPrimaryKey();
            if (ArtifactEnum.CONCEPTUAL_ENTITY.getArtifactType().intValue() == convertType) {
                //概念->逻辑
                info.getAttrs().put(DataModelEntity.ENTITY_TYPE, LOGIC);
                JSONArray array = JSON.parseArray(ciPrimaryKey);
                array.set(0, LOGICENTITY);
                array.add(LOGIC);
                info.setCiPrimaryKey(array.toString());
            }
            if (ArtifactEnum.RELATION_ENTITY.getArtifactType().intValue() == convertType) {
                info.getAttrs().put(DataModelEntity.ENTITY_TYPE, SYSTEM);
                info.setCiPrimaryKey(ciPrimaryKey.replace(LOGIC, SYSTEM));
            }
            if (ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().intValue() == convertType) {
                info.getAttrs().remove(DataModelEntity.ENTITY_TYPE);
                JSONArray array = JSON.parseArray(ciPrimaryKey);
                array.set(0, PHYSICALENTITY);
                array.remove(SYSTEM);
                info.setCiPrimaryKey(array.toString());
            }
            //新主键，半旧实体属性===除了类型属性被更改，其他属性都是旧的，可以用；
            entityMap.put(info.getCiPrimaryKey(), info);
        }
        //替换完实体的主键信息，然后去查询看数据量是否对的上，对上之后，再拿查出来的实体信息 进行全量属性值比对；
        List<String> entityPrimaryKeys = entityInfo.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList());
       List<ESCIInfo> newEntityInfos = iciSwitchSvc.getCiByPrimaryKeys(entityPrimaryKeys, loginCode, LibType.PRIVATE);
        //第一次转图
        if (BinaryUtils.isEmpty(newEntityInfos)) {
            return response;
        }
       //非第一次转图：个数不一致：概念图中有新增，逻辑图中没有，【逻辑图中有新增判断不出来】
        if (!BinaryUtils.isEmpty(newEntityInfos) && newEntityInfos.size() != entityInfo.size()) {
            response.setUpdate(true);
            return response;
        }
        //非第一次转图：个数一致，校验全属性
        if (!BinaryUtils.isEmpty(newEntityInfos) && newEntityInfos.size() == entityInfo.size()) {
            for (ESCIInfo newEntityInfo : newEntityInfos) {
                ESCIInfo oldInfo = entityMap.get(newEntityInfo.getCiPrimaryKey());
                Map<String, Object> oldEntityAttr = oldInfo.getAttrs();
                //旧属性里面应该有逻辑实体和系统逻辑实体中的类型属性，去除；
                oldEntityAttr.remove(DataModelEntity.ENTITY_TYPE);
                //------待确定----概念转逻辑时，先不对比实体类别 类型
                newEntityInfo.getAttrs().remove(DataModelEntity.ENTITY_TYPE);
                if(convertType == 6){
                    newEntityInfo.getAttrs().remove(DataModelEntity.ENTITY_CATEGORY);
                }
                if (!newEntityInfo.getAttrs().equals(oldEntityAttr)) {
                    response.setUpdate(true);
                    return response;
                }
                //走到这步，说明实体信息没有变化，继续校验属性，可以直接用查出来的新实体分类数据继续处理；
                oldAndNewEntityMap.put(oldInfo.getCiCode(), newEntityInfo.getCiCode());
            }
        }

        /**
         * 查询实体和属性数据，ciCode应该会变，收集所有的主键信息，去对应的转换类型的分类中去查询，
         * 如果有查不到的数据，表示概念图中 有新增（提示）， 如果是逻辑图中有新增（原5-7），再次转的时候，就是图中数据是5个，左侧列表是7个；
         * 如果数据都查到了，（数据量可能没变，也可能在逻辑图中有新增【这种应该不用管】），这时候校验ci数据的全量属性值，是否有更新，如果有更新，提示
         * 走到这表示 实体数据有 且没有变更，接下来看属性信息是否有变更
         * 统计原概念实体下得 属性得个数，统计新实体下面的属性得个数；
         */
        List<String> attrCiCode = attrCiCodeMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        if (BinaryUtils.isEmpty(attrCiCode)) {
            return response;
        }
        List<ESCIInfo> attrInfo = iciSwitchSvc.getCiByCodes(attrCiCode, loginCode, LibType.PRIVATE);
        //校验完实体---再校验属性，如果无属性，则不必走下面的流程；
        //新主键--旧实体；
        Map<String, Map<String, Object>> attrMap = new HashMap<>();
        for (ESCIInfo attr : attrInfo) {
            //属性所属实体的ciCode,主键必填，不会为空
            String entityCode = attr.getAttrs().get(DataModelAttribute.ENTITY).toString();
            String primaryKey = attr.getCiPrimaryKey();
            JSONArray array = JSON.parseArray(primaryKey);
            array.remove(entityCode);
            if (!BinaryUtils.isEmpty(oldAndNewEntityMap.get(entityCode))) {
                array.add(oldAndNewEntityMap.get(entityCode));
                attr.setCiPrimaryKey(array.toString());
                //这个位置，更新了主键，但还未更新属性
                attrMap.put(attr.getCiPrimaryKey(), attr.getAttrs());
                attr.getAttrs().put(DataModelAttribute.ENTITY, oldAndNewEntityMap.get(entityCode));
            }
        }
        //将属性的主键抽出来
        List<String> attrPrimarykeys = attrInfo.stream().map(ESCIInfo::getCiPrimaryKey).collect(Collectors.toList());
        List<ESCIInfo> newAttrInfo = iciSwitchSvc.getCiByPrimaryKeys(attrPrimarykeys, loginCode, LibType.PRIVATE);
        if (newAttrInfo.size() != attrInfo.size()) {
            //属性数据有被删除
            response.setUpdate(true);
            return response;
        }
        if (!BinaryUtils.isEmpty(newAttrInfo) && newAttrInfo.size() == attrInfo.size()) {
            //属性数据条数没有变化，继续对比全字段属性值是否有变化；
            for (ESCIInfo newAttr : newAttrInfo) {
                Map<String, Object> attrInMap = attrMap.get(newAttr.getCiPrimaryKey());
                attrInMap.remove(DataModelAttribute.ENTITY);
                //比较全字段
                newAttr.getAttrs().remove(DataModelAttribute.ENTITY);
                if (!newAttr.getAttrs().toString().equals(attrInMap.toString())) {
                    response.setUpdate(true);
                    return response;
                }
            }
        }
        return response;
    }

    @Override
    public Map<String, List<DataTypeDto>> queryDataType(LibType libType) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //查询所有类型分类的ci数据，按类型分类，然后取出每一种
        CcCiClassInfo classInfo = ciClassApiSvc.getCIClassByCodes(Env.DATA_TYPE.getCode());
        Long classId = classInfo.getCiClass().getId();
        CCcCi ci = new CCcCi();
        ci.setClassId(classId);
        if (libType.equals(LibType.PRIVATE)) {
            ci.setOwnerCodeEqual(loginCode);
        }
        List<CcCiInfo> ccCiInfos = iciSwitchSvc.queryCiInfoList(domainId, ci, "", true, false, libType);
        //看下这块
        BinaryUtils.isEmpty(ccCiInfos);
        Map<String, List<DataTypeDto>> map = new HashMap<>(16);
        List<DataTypeDto> dtoList;
        for (CcCiInfo ccCiInfo : ccCiInfos) {
            String typeKey = ccCiInfo.getAttrs().get(DataModelDataType.BELONG_CLASS);
            if (BinaryUtils.isEmpty(typeKey)) {
                continue;
            }
            if (BinaryUtils.isEmpty(map.get(typeKey))) {
                dtoList = new ArrayList<>();
            } else {
                dtoList = map.get(typeKey);
            }
            DataTypeDto dataType = new DataTypeDto();
            dataType.setCiCode(ccCiInfo.getCi().getCiCode());
            dataType.setName(ccCiInfo.getAttrs().get(DataModelDataType.NAME_CN));
            dtoList.add(dataType);
            map.put(typeKey, dtoList);
        }

        return map;
    }

    @Override
    public List<CcCiInfo> getAttrInfoBiCiCode(AttrParamDto paramDto) {
        //获取数据标准和实体属性的分类shape
        CcCiClassInfo standard = ciClassApiSvc.getCIClassByCodes(Env.STANDARD.getCode());
        CcCiClassInfo entityAttr = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        CCcCi ci = new CCcCi();
        ci.setClassId(entityAttr.getCiClass().getId());
        ci.setCiCodes(paramDto.getCiCodes().toArray(new String[]{}));
        ci.setOwnerCodeEqual(paramDto.getOwnerCode());
        List<CcCiInfo> ciList = iciSwitchSvc.queryCiInfoList(domainId, ci, "", true, false, paramDto.getLibType());
        if(BinaryUtils.isEmpty(ciList)){
            return new ArrayList<>();
        }
        List<CcCiInfo> list = new ArrayList<>();
        for (CcCiInfo info : ciList) {
            if(info.getAttrs().get("数据标准") != null){
                info.getCi().setShape(standard.getCiClass().getShape());
            }
            info.getCi().setShape(entityAttr.getCiClass().getShape());
            CcCiInfo copy = EamUtil.copy(info, CcCiInfo.class);
            list.add(copy);
        }
        list.sort(Comparator.comparing(each -> paramDto.getCiCodes().indexOf(each.getCi().getCiCode())));
        return list;
    }
}
